﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BambuBusParser
{
    public class Crc8
    {
        private byte _polynome;
        private byte _initial;
        private byte _xorOut;
        private bool _reverseIn;
        private bool _reverseOut;
        private byte _crc;
        private uint _count;

        // Constructor
        public Crc8(byte polynome = 0x07, byte initial = 0x00, byte xorOut = 0x00, bool reverseIn = false, bool reverseOut = false)
        {
            _polynome = polynome;
            _initial = initial;
            _xorOut = xorOut;
            _reverseIn = reverseIn;
            _reverseOut = reverseOut;
            Restart();
        }

        // Reset the CRC parameters
        public void Reset(byte polynome, byte initial, byte xorOut, bool reverseIn, bool reverseOut)
        {
            _polynome = polynome;
            _initial = initial;
            _xorOut = xorOut;
            _reverseIn = reverseIn;
            _reverseOut = reverseOut;
            Restart();
        }

        // Restart the CRC calculation
        public void Restart()
        {
            _crc = _initial;
            _count = 0;
        }

        // Calculate the CRC8
        public byte Calc()
        {
            var result = _crc;
            if (_reverseOut) result = ReverseBits(result);
            result ^= _xorOut;
            return result;
        }

        // Get the current count of processed bytes
        public uint Count => _count;

        // Add a single byte to the CRC calculation
        public void Add(byte value)
        {
            _count++;
            AddInternal(value);
        }

        // Add an array of bytes to the CRC calculation
        public void Add(byte[] array)
        {
            _count += (uint)array.Length;
            foreach (var value in array)
            {
                AddInternal(value);
            }
        }

        // Add an array of bytes with yield support for periodic yielding
        public void Add(byte[] array, uint yieldPeriod)
        {
            _count += (uint)array.Length;
            var period = yieldPeriod;
            foreach (var value in array)
            {
                AddInternal(value);
                if (--period == 0)
                {
                    // Call yield here if needed (e.g., in a real-time environment)
                    period = yieldPeriod;
                }
            }
        }

        // Internal method to handle adding a byte
        private void AddInternal(byte value)
        {
            if (_reverseIn) value = ReverseBits(value);
            _crc ^= value;

            for (var i = 8; i > 0; i--)
            {
                if ((_crc & 0x80) != 0)
                {
                    _crc <<= 1;
                    _crc ^= _polynome;
                }
                else
                {
                    _crc <<= 1;
                }
            }
        }

        // Get the current CRC value
        public byte GetCrc() => Calc();

        // Reverse bits of a byte (bit-reversal)
        private static byte ReverseBits(byte value)
        {
            byte result = 0;
            for (var i = 0; i < 8; i++)
            {
                result = (byte)((result << 1) | (value & 1));
                value >>= 1;
            }
            return result;
        }
    }


}
